package cn.tedu._05springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Aspect注解：
 * 1.标识当前类为切面类，为目标方法植入额外扩展功能的类
 *
 * Order注解:
 *   多个切面类时,指定切面的优先级,数字越大,优先级越低;
 */
@Order(10)
@Aspect
@Component
public class LogAspect {
    /*
        自定义切入点表达式的方法，指定哪些目标方法需要被增强
     */
    @Pointcut("execution(public int cn.tedu._05springaop.aop.CalculatorImpl.*(..))")
    public void doTime(){}


    /**
     * Before注解：前置通知，在目标方法执行执行执行；
     * joinPoint参数：连接点对象，即拦截到的目标方法对象
     */
    @Before("doTime()")  //切入点表达式
    public void before(JoinPoint joinPoint){
        /*
            获取目标方法的的名称和参数
            1.获取方法名： joinPoint.getSignature().getName();
            2.获取参数列表：joinPoint.getArgs()
         */
        String name = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());

        System.out.println("[前置通知]:" + name + "方法开始，参数为:" + args);
    }

    /**
     * After注解：后置通知，在目标方法结束后执行[正常结束|异常结束]；
     */
    @After("doTime()")
    public void after(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        //后置通知没有权限获取目标方法的返回值
        System.out.println("[后置通知]：" + name + "方法执行结束");
    }

    /**
     * AfterReturning注解：返回通知
     *  1.目标方法正常结束后执行
     *  2.有权限获取到目标方法的返回值
     */
    @AfterReturning(value = "doTime()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("[返回通知]：" + name + "方法执行结束，结果为：" + result);
    }

    /**
     * AfterThrowing注解：异常通知
     *  1.目标方法抛出异常时执行
     *  2.有权限获取异常信息
     */
    @AfterThrowing(value = "doTime()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Throwable ex){
        System.out.println("[异常通知]:目标方法抛出异常，信息为：" + ex.getMessage());
    }

    /**
     * Around注解：环绕通知
     *  1.等价于  前置通知+返回通知+异常通知+后置通知；
     *  2.通常包裹在  try...catch...finally...语句中
     *  ProceedingJoinPoint类是JoinPoint的子类，定义了可以执行目标方法的方法
     */
    @Around("doTime()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知-前置");
            result = joinPoint.proceed();  //执行目标方法，得到目标方法的返回值
            System.out.println("环绕通知-返回");
        } catch (Throwable e) {
            System.out.println("环绕通知-异常");
            throw new RuntimeException(e);
        }finally {
            System.out.println("环绕通知-后置");
        }
        return  result;
    }


}
